import Foundation

class TcpIoManager: ObservableObject {
    
    private var timer: DispatchSourceTimer?
    private var tcpManager: TcpManager?
    private var numDI: Int
    private var numDO: Int
    private var isRequestInProgress = false
    private var waitInterval: Double = 0.02
    
    var outStatus: [Bool] = []
    var inStatus: [Bool] = []
    
    init(ipAddress: String, port: Int, numDI: Int = 12, numDO: Int = 16) {
        self.numDI = numDI
        self.numDO = numDO
        self.outStatus = Array(repeating: false, count: self.numDO)
        self.inStatus = Array(repeating: false, count: self.numDI)
        
        self.tcpManager = TcpManager(ipAddress: ipAddress, port: port, timeout: 1)
        self.tcpManager?.deprecateStreamRead()
        
     //   if tcpManager != nil {           self.startTimer()}
    }
    
     func closeManually() {
        tcpManager?.closeManually()
    }
    
    private func startTimer() {
        let queue = DispatchQueue(label: "com.hardwaretests.tcpIoTimer", qos: .background ,attributes: .concurrent)
        timer = DispatchSource.makeTimerSource(queue: queue)
        timer?.schedule(deadline: .now(), repeating: .milliseconds(10)) // Adjust interval to 50ms
        
        timer?.setEventHandler { [weak self] in
            guard let self = self, !self.isRequestInProgress else { return }
            self.isRequestInProgress = true
            
            DispatchQueue.global().async {
                let dis = self.getAllDI()
                let dos = self.getAllDO()
                
                DispatchQueue.main.async {
                    if ((!dis.isEmpty) && (!dos.isEmpty)) {
                        self.objectWillChange.send()
                    }
                    self.isRequestInProgress = false
                }
            }
        }
        
        timer?.resume()
    }
    
   // deinit {
       // timer?.cancel()
   // }
    
    func getDI(point: Int, station: Int = 1) -> Bool {
        getAllDI(station: station)
        return inStatus[point - 1]
    }
    func getDO(point: Int, station: Int = 1) -> Bool {
        getAllDO(station: station)
        return outStatus[point - 1]
    }
     func getAllDI(station: Int = 1) -> [Bool] {
        var cmd: [UInt8] = Array(repeating: 0x00, count: 6)
        cmd[0] = UInt8(station)
        cmd[1] = 0x02
        cmd[2] = 0x00
        cmd[3] = 0x00
        cmd[4] = 0x00
        cmd[5] = UInt8(self.numDI)

        let crc = calculateCRC16Modbus(input: cmd)
        cmd.append(contentsOf: crc)
        
        let semaphore = DispatchSemaphore(value: 0)

        tcpManager?.sendUInt8(dataToSend: cmd, bytesToRead: 7) { [self] isSuccess, response in
            let str = uInt8ToString(byteArray: response)
           // print("response: ", response.count, response, str)
            if isSuccess {
                if (response.count == 7) {
                    let low = response[3]
                    let high = response[4]
                    
                    if numDI > 8 {
                        for i in 0..<8 {
                            inStatus[i] = (low & (1 << i)) != 0
                        }
                        for i in 8..<numDI {
                            inStatus[i] = (high & (1 << (i - 8))) != 0
                        }
                    } else {
                        for i in 0..<numDI {
                            inStatus[i] = (low & (1 << i)) != 0
                        }
                    }
                }
            }
            semaphore.signal()
        }

        _ = semaphore.wait(timeout: .now() + waitInterval)
        return inStatus
    }

     func getAllDO(station: Int = 1) -> [Bool] {
        var cmd: [UInt8] = Array(repeating: 0x00, count: 6)
        cmd[0] = UInt8(station)
        cmd[1] = 0x01
        cmd[2] = 0x00
        cmd[3] = 0x00
        cmd[4] = 0x00
        cmd[5] = UInt8(self.numDO)

        let crc = calculateCRC16Modbus(input: cmd)
        cmd.append(contentsOf: crc)

        let semaphore = DispatchSemaphore(value: 0)

        tcpManager?.sendUInt8(dataToSend: cmd, bytesToRead: 7) { [self] isSuccess, response in
            let str = uInt8ToString(byteArray: response)
            print("response: ", response.count,  response)
            if isSuccess {
                if (response.count == 7) {
                    let low = response[3]
                    let high = response[4]
                    
                    if numDO > 8 {
                        for i in 0..<8 {
                            outStatus[i] = (low & (1 << i)) != 0
                        }
                        for i in 8..<numDO {
                            outStatus[i] = (high & (1 << (i - 8))) != 0
                        }
                    } else {
                        for i in 0..<numDO {
                            outStatus[i] = (low & (1 << i)) != 0
                        }
                    }
                }
            }
            semaphore.signal()
        }

        _ = semaphore.wait(timeout: .now() + waitInterval)
        return outStatus
    }

    func SetDO(point: Int, status: Bool, station: Int = 1) -> Bool {
        var cmd: [UInt8] = Array(repeating: 0x00, count: 6)
        cmd[0] = UInt8(station)
        cmd[1] = 0x05
        cmd[2] = 0x00
        cmd[3] = UInt8(point - 1)
        cmd[4] = status ? 0xFF : 0x00
        cmd[5] = status ? 0x00 : 0xFF
        let crc = calculateCRC16Modbus(input: cmd)
        cmd.append(contentsOf: crc)

        let semaphore = DispatchSemaphore(value: 0)
        var flag = false

        tcpManager?.sendUInt8(dataToSend: cmd, bytesToRead: 8) { isSuccess, response in
            
            let str = uInt8ToString(byteArray: response)
            print("response: ", response.count,  str)
            
            if isSuccess && response == cmd {
                flag = true
            }
            semaphore.signal()
        }

        _ = semaphore.wait(timeout:  .now() + waitInterval)
        return flag
    }
    
    
    func SetAllDO(status: UInt16, station: Int = 1) -> Bool {
        var cmd: [UInt8] = Array(repeating: 0x00, count: 9)
        cmd[0] = UInt8(station)
        cmd[1] = 0x0F
        cmd[2] = 0x00
        cmd[3] = 0x00
        cmd[4] = 0x00
        cmd[5] = UInt8(self.numDO)
        cmd[6] = 0x02
        cmd[7] = UInt8(status & 0xff)
        cmd[8] = UInt8(status >> 8)
        let crc = calculateCRC16Modbus(input: cmd)
        cmd.append(contentsOf: crc)

        let semaphore = DispatchSemaphore(value: 0)
        var flag = false

        tcpManager?.sendUInt8(dataToSend: cmd, bytesToRead: 8) { isSuccess, response in
            
            let str = uInt8ToString(byteArray: response)
            print("response: ", response.count,  str)
            
            if isSuccess && response == cmd {
                flag = true
            }
            semaphore.signal()
        }

        _ = semaphore.wait(timeout:  .now() + waitInterval)
        return flag
    }
    
}
